

#include "v4l_interface.hpp"



namespace imp {
	namespace gui {

		void V4LInterface::__block_control_signals(std::list< ControlHolder >& list, bool block) {

			std::list< ControlHolder >::iterator iter;

			for (iter = list.begin(); iter != list.end(); ++iter) {
				(*iter).con.block(block);
			}
		}

		bool V4LInterface::set_control_hscale(int type, double val, std::list< ControlHolder > * list, Gtk::Widget * wctrl) {
			std::list< ControlHolder >::iterator iter;

			for (iter = list->begin(); iter != list->end(); ++iter) {
				if ((*iter).widget == wctrl) break;
			}

			int value = static_cast<Gtk::HScale *> (wctrl)->get_value();
			struct v4l2_queryctrl qctrl = (*iter).qctrl;

			int r = v4lcap.set_control(qctrl.id, value);

			__update_control_widgets(*list);

			return true;

		}

		void V4LInterface::set_control(std::list< ControlHolder > * list, Gtk::Widget * wctrl) {
			std::list<ControlHolder >::iterator iter;

			for (iter = list->begin(); iter != list->end(); ++iter) {
				if ((*iter).widget == wctrl) break;
			}

			int value, r;
			struct v4l2_queryctrl qctrl = (*iter).qctrl;

			switch (qctrl.type) {
				case V4L2_CTRL_TYPE_INTEGER:
				case V4L2_CTRL_TYPE_INTEGER64:

					value = static_cast<Gtk::HScale *> (wctrl)->get_value();
					r = v4lcap.set_control(qctrl.id, value);

					break;

				case V4L2_CTRL_TYPE_BOOLEAN:

					value = static_cast<Gtk::CheckButton *> (wctrl)->get_active();
					r = v4lcap.set_control(qctrl.id, value);

					break;

				case V4L2_CTRL_TYPE_BUTTON:

					r = v4lcap.set_control(qctrl.id, 1);


					break;

				case V4L2_CTRL_TYPE_STRING:


					break;

				case V4L2_CTRL_TYPE_MENU:

					Gtk::TreeModel::Children::iterator iter = static_cast<Gtk::ComboBox *> (wctrl)->get_active();
					Gtk::TreeModel::Row row = *iter;
					struct v4l2_querymenu qm;
					qm = row[model_control_menu.m_col_data];
					r = v4lcap.set_control(qctrl.id, qm.index);

					break;

			}

			__update_control_widgets(*list);


		}

		/* = Auxiliary Methods ============================================== */
		void V4LInterface::__update_control_widgets(std::list< ControlHolder >& ctrl_list) {

			Gtk::Widget * wctrl = NULL;
			struct v4l2_queryctrl qctrl;
			struct v4l2_control control;

			__block_control_signals(ctrl_list, true);

			std::list< ControlHolder >::iterator iter;

			for (iter = ctrl_list.begin(); iter != ctrl_list.end(); ++iter) {
				qctrl = (*iter).qctrl;
				wctrl = (*iter).widget;

				v4lcap.get_control(&control, qctrl.id);

				switch (qctrl.type) {
					case V4L2_CTRL_TYPE_INTEGER:
					case V4L2_CTRL_TYPE_INTEGER64:

						static_cast<Gtk::HScale *> (wctrl)->set_value(control.value);
						break;

					case V4L2_CTRL_TYPE_BOOLEAN:

						static_cast<Gtk::CheckButton *> (wctrl)->set_active(control.value == 1);
						break;

					case V4L2_CTRL_TYPE_BUTTON:

						break;

					case V4L2_CTRL_TYPE_STRING:

						break;

					case V4L2_CTRL_TYPE_MENU:


						Glib::RefPtr<Gtk::TreeModel> model = static_cast<Gtk::ComboBox *> (wctrl)->get_model();

						Gtk::TreeModel::Children child = model->children();

						int i = 0;
						for (Gtk::TreeModel::Children::iterator iter = child.begin(); iter != child.end(); ++iter, ++i) {

							Gtk::TreeModel::Row row = *iter;
							struct v4l2_querymenu qm;
							qm = row[model_control_menu.m_col_data];

							if (control.value == qm.index) {
								static_cast<Gtk::ComboBox *> (wctrl)->set_active(iter);
								break;
							}

						}







						break;

				}
			}
			__block_control_signals(ctrl_list, false);

		}

		void V4LInterface::__create_control_lists() {

			int size;
			__create_default_control_list();
			size = ctrl_list_default.size();

			__create_user_control_list();
			size = ctrl_list_user.size();

			__create_private_control_list();
			size = ctrl_list_private.size();


			size = ctrl_list_default.size();
			size = ctrl_list_user.size();
			size = ctrl_list_private.size();
		}

		void V4LInterface::__create_default_control_list() {

			ctrl_list_default.clear();
			struct v4l2_queryctrl qctrl;

			ControlHolder hold;

			if (v4lcap.enum_control_default(&qctrl, true)) {
				do {

					hold.qctrl = qctrl;
					Gtk::Widget * wctrl;

					switch (qctrl.type) {
						case V4L2_CTRL_TYPE_INTEGER:
						case V4L2_CTRL_TYPE_INTEGER64:

							wctrl = new Gtk::HScale();

							static_cast<Gtk::HScale *> (wctrl)->set_value_pos(Gtk::POS_RIGHT);
							static_cast<Gtk::HScale *> (wctrl)->set_range(qctrl.minimum, qctrl.maximum);
							static_cast<Gtk::HScale *> (wctrl)->set_increments(qctrl.step, 10 * qctrl.step);

							hold.con = static_cast<Gtk::HScale *> (wctrl)->signal_change_value().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control_hscale), &ctrl_list_default, wctrl)
									);



							break;

						case V4L2_CTRL_TYPE_BOOLEAN:

							wctrl = new Gtk::CheckButton((const char *) qctrl.name);
							hold.con = static_cast<Gtk::CheckButton *> (wctrl)->signal_clicked().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

						case V4L2_CTRL_TYPE_BUTTON:

							wctrl = new Gtk::Button((const char *) qctrl.name);

							hold.con = static_cast<Gtk::Button *> (wctrl)->signal_clicked().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

						case V4L2_CTRL_TYPE_STRING:

							wctrl = new Gtk::Label((const char *) qctrl.name);
							break;

						case V4L2_CTRL_TYPE_MENU:

							wctrl = new Gtk::ComboBox();
							Glib::RefPtr<Gtk::ListStore> lstore = Gtk::ListStore::create(model_control_menu);
							static_cast<Gtk::ComboBox *> (wctrl)->set_model(lstore);
							static_cast<Gtk::ComboBox *> (wctrl)->pack_start(model_control_menu.m_col_name);

							int i;
							for (i = qctrl.minimum; i <= qctrl.maximum; i++) {

								struct v4l2_querymenu qm;

								if (!v4lcap.get_control_menu(&qm, qctrl.id, i)) {
									continue;
								}

								Gtk::TreeModel::Row row = *(lstore->append());
								Glib::ustring name = (const char *) qm.name;
								row[model_control_menu.m_col_name] = name;
								row[model_control_menu.m_col_data] = qm;
							}

							hold.con = static_cast<Gtk::ComboBox *> (wctrl)->signal_changed().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

					}

					hold.widget = wctrl;
					ctrl_list_default.push_back(hold);

				} while (v4lcap.enum_control_default(&qctrl, false));
			}

		}

		void V4LInterface::__create_user_control_list() {

			ctrl_list_user.clear();
			struct v4l2_queryctrl qctrl;

			ControlHolder hold;

			if (v4lcap.enum_control_user(&qctrl, true)) {
				do {

					hold.qctrl = qctrl;
					Gtk::Widget * wctrl;

					switch (qctrl.type) {
						case V4L2_CTRL_TYPE_INTEGER:
						case V4L2_CTRL_TYPE_INTEGER64:

							wctrl = new Gtk::HScale();

							static_cast<Gtk::HScale *> (wctrl)->set_value_pos(Gtk::POS_RIGHT);
							static_cast<Gtk::HScale *> (wctrl)->set_range(qctrl.minimum, qctrl.maximum);
							static_cast<Gtk::HScale *> (wctrl)->set_increments(qctrl.step, 10 * qctrl.step);

							hold.con = static_cast<Gtk::HScale *> (wctrl)->signal_change_value().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control_hscale), &ctrl_list_default, wctrl)
									);



							break;

						case V4L2_CTRL_TYPE_BOOLEAN:

							wctrl = new Gtk::CheckButton((const char *) qctrl.name);
							hold.con = static_cast<Gtk::CheckButton *> (wctrl)->signal_clicked().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

						case V4L2_CTRL_TYPE_BUTTON:

							wctrl = new Gtk::Button((const char *) qctrl.name);

							hold.con = static_cast<Gtk::Button *> (wctrl)->signal_clicked().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

						case V4L2_CTRL_TYPE_STRING:

							wctrl = new Gtk::Label((const char *) qctrl.name);
							break;

						case V4L2_CTRL_TYPE_MENU:

							wctrl = new Gtk::ComboBox();
							Glib::RefPtr<Gtk::ListStore> lstore = Gtk::ListStore::create(model_control_menu);
							static_cast<Gtk::ComboBox *> (wctrl)->set_model(lstore);
							static_cast<Gtk::ComboBox *> (wctrl)->pack_start(model_control_menu.m_col_name);

							int i;
							for (i = qctrl.minimum; i <= qctrl.maximum; i++) {

								struct v4l2_querymenu qm;

								if (!v4lcap.get_control_menu(&qm, qctrl.id, i)) {
									continue;
								}

								Gtk::TreeModel::Row row = *(lstore->append());
								Glib::ustring name = (const char *) qm.name;
								row[model_control_menu.m_col_name] = name;
								row[model_control_menu.m_col_data] = qm;
							}

							hold.con = static_cast<Gtk::ComboBox *> (wctrl)->signal_changed().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

					}

					hold.widget = wctrl;
					ctrl_list_user.push_back(hold);

				} while (v4lcap.enum_control_user(&qctrl, false));
			}

		}

		void V4LInterface::__create_private_control_list() {

			ctrl_list_private.clear();
			struct v4l2_queryctrl qctrl;

			ControlHolder hold;

			if (v4lcap.enum_control_private(&qctrl, true)) {
				do {

					hold.qctrl = qctrl;
					Gtk::Widget * wctrl;

					switch (qctrl.type) {
						case V4L2_CTRL_TYPE_INTEGER:
						case V4L2_CTRL_TYPE_INTEGER64:

							wctrl = new Gtk::HScale();

							static_cast<Gtk::HScale *> (wctrl)->set_value_pos(Gtk::POS_RIGHT);
							static_cast<Gtk::HScale *> (wctrl)->set_range(qctrl.minimum, qctrl.maximum);
							static_cast<Gtk::HScale *> (wctrl)->set_increments(qctrl.step, 10 * qctrl.step);

							hold.con = static_cast<Gtk::HScale *> (wctrl)->signal_change_value().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control_hscale), &ctrl_list_default, wctrl)
									);



							break;

						case V4L2_CTRL_TYPE_BOOLEAN:

							wctrl = new Gtk::CheckButton((const char *) qctrl.name);
							hold.con = static_cast<Gtk::CheckButton *> (wctrl)->signal_clicked().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

						case V4L2_CTRL_TYPE_BUTTON:

							wctrl = new Gtk::Button((const char *) qctrl.name);

							hold.con = static_cast<Gtk::Button *> (wctrl)->signal_clicked().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

						case V4L2_CTRL_TYPE_STRING:

							wctrl = new Gtk::Label((const char *) qctrl.name);
							break;

						case V4L2_CTRL_TYPE_MENU:

							wctrl = new Gtk::ComboBox();
							Glib::RefPtr<Gtk::ListStore> lstore = Gtk::ListStore::create(model_control_menu);
							static_cast<Gtk::ComboBox *> (wctrl)->set_model(lstore);
							static_cast<Gtk::ComboBox *> (wctrl)->pack_start(model_control_menu.m_col_name);

							int i;
							for (i = qctrl.minimum; i <= qctrl.maximum; i++) {

								struct v4l2_querymenu qm;

								if (!v4lcap.get_control_menu(&qm, qctrl.id, i)) {
									continue;
								}

								Gtk::TreeModel::Row row = *(lstore->append());
								Glib::ustring name = (const char *) qm.name;
								row[model_control_menu.m_col_name] = name;
								row[model_control_menu.m_col_data] = qm;
							}

							hold.con = static_cast<Gtk::ComboBox *> (wctrl)->signal_changed().connect(

									sigc::bind< std::list< ControlHolder > *, Gtk::Widget *>(
									sigc::mem_fun(*this, &V4LInterface::set_control), &ctrl_list_default, wctrl)
									);

							break;

					}

					hold.widget = wctrl;
					ctrl_list_private.push_back(hold);

				} while (v4lcap.enum_control_private(&qctrl, false));
			}

		}

		void V4LInterface::__make_control_table(std::list< ControlHolder >& list, const char * title) {

			// create new controls =====================================================		
			Gtk::Label * label = NULL;
			Gtk::Table * table = NULL;
			Gtk::Widget * wctrl = NULL;

			int ROWS = 4;
			int COLS = 2;
			int r, c;
			int tab_count = 0;
			int count = 0;

			struct v4l2_queryctrl qctrl;
			std::list< ControlHolder >::iterator iter;

			for (iter = list.begin(); iter != list.end(); ++iter) {
				qctrl = (*iter).qctrl;
				wctrl = (*iter).widget;

				// create a new page of controls
				if (count % (ROWS * COLS) == 0) {
					r = 0;
					c = 0;
					count = 0;
					table = new Gtk::Table(ROWS, COLS * 2, TRUE);
					notebook.append_page(*table, Glib::ustring::compose("%1 %2", title, tab_count++), false);
				}

				r = count / COLS;
				c = count % COLS;

				c = c * 2;

				wctrl->set_size_request(120, -1);

				if (qctrl.type == V4L2_CTRL_TYPE_BOOLEAN || qctrl.type == V4L2_CTRL_TYPE_BUTTON) {

					table->attach(*wctrl, c, c + 2, r, r + 1, Gtk::FILL, Gtk::SHRINK, 0, 0);


				} else {

					label = new Gtk::Label((const char *) qctrl.name);

					table->attach(*label, c, c + 1, r, r + 1, Gtk::FILL, Gtk::SHRINK, 0, 0);
					table->attach(*wctrl, c + 1, c + 2, r, r + 1, Gtk::FILL, Gtk::SHRINK, 0, 0);

					label->set_size_request(130, -1);
					//label->set_max_width_chars(10);
					label->set_alignment(1.0, 0.5);

				}

				count++;

			}

			notebook.show_all();

		}

		/* ================================================================== */

		void V4LInterface::__update_cb_input() {

			cb_input_signal.block(true);

			ls_input->clear();

			struct v4l2_input input;
			v4lcap.get_input(&input);
			int index = input.index;

			if (v4lcap.enum_input(&input, true)) {
				do {
					Gtk::TreeModel::Row row = *(ls_input->append());
					Glib::ustring name = (const char *) input.name;
					row[model_input.m_col_name] = name;
					row[model_input.m_col_data] = input;

					if (input.index == index) {
						cb_input.set_active(row);
					}

				} while (v4lcap.enum_input(&input, false));
			}

			cb_input_signal.block(false);

		}

		void V4LInterface::__update_cb_standard() {

			cb_standard_signal.block(true);

			ls_standard->clear();

			struct v4l2_standard standard;

			v4lcap.get_standard(&standard);
			int index = standard.id;

			if (v4lcap.enum_standard(&standard, true)) {
				do {
					Gtk::TreeModel::Row row = *(ls_standard->append());
					Glib::ustring name = (const char *) standard.name;
					row[model_standard.m_col_name] = name;
					row[model_standard.m_col_data] = standard;

					if (standard.id == index) {
						cb_standard.set_active(row);
					}

				} while (v4lcap.enum_standard(&standard, false));
			}

			cb_standard_signal.block(false);
		}

		void V4LInterface::__update_cb_format_desc() {

			cb_format_desc_signal.block(true);

			ls_format_desc->clear();

			struct v4l2_fmtdesc fmt_desc;
			v4lcap.get_format_desc(&fmt_desc);
			int index = fmt_desc.pixelformat;

			if (v4lcap.enum_format_desc(&fmt_desc, true)) {
				do {
					Gtk::TreeModel::Row row = *(ls_format_desc->append());
					Glib::ustring name = (const char *) fmt_desc.description;
					row[model_format_desc.m_col_name] = name;
					row[model_format_desc.m_col_data] = fmt_desc;

					if (fmt_desc.pixelformat == index) {
						cb_format_desc.set_active(row);
					}

				} while (v4lcap.enum_format_desc(&fmt_desc, false));
			}

			cb_format_desc_signal.block(false);

		}

		void V4LInterface::__update_cb_frmsize() {

			cb_frmsize_signal.block(true);

			ls_frmsize->clear();

			struct v4l2_format format;
			v4lcap.get_format(&format);
			sp_width.set_value((double) format.fmt.pix.width);
			sp_height.set_value((double) format.fmt.pix.height);

			struct v4l2_frmsizeenum frmsize;

			if (v4lcap.enum_frame_size(&frmsize, true)) {
				do {
					Gtk::TreeModel::Row row = *(ls_frmsize->append());
					Glib::ustring name = Glib::ustring::compose("%1x%2", frmsize.discrete.width, frmsize.discrete.height);
					row[model_frame_size.m_col_name] = name;
					row[model_frame_size.m_col_data] = frmsize;

					if (frmsize.discrete.width == format.fmt.pix.width && frmsize.discrete.height == format.fmt.pix.height) {
						cb_frame_size.set_active(row);
					}

				} while (v4lcap.enum_frame_size(&frmsize, false));
			}
			cb_frmsize_signal.block(false);
		}

		void V4LInterface::__update_capabilities() {


			__update_cb_input();
			__update_cb_standard();
			__update_cb_format_desc();
			__update_cb_frmsize();


		}

		/* = Events ========================================================= */
		void V4LInterface::event_bt_start_clicked() {

			if (!v4lcap.is_opened()) return;

			Glib::ustring label = bt_start.get_label();

			if (0 == label.compare("start")) {

				if (!v4lcap.start()) return;

				struct v4l2_format format;
				v4lcap.get_format(&format);

				sp_width.set_value(format.fmt.pix.width);
				sp_height.set_value(format.fmt.pix.height);


				bt_start.set_label("stop");
				cb_device.set_sensitive(false);
				cb_input.set_sensitive(false);
				cb_standard.set_sensitive(false);
				cb_frame_size.set_sensitive(false);
				cb_format_desc.set_sensitive(false);
				sp_width.set_sensitive(false);
				sp_height.set_sensitive(false);
				m_signal_start.emit(true);

			} else {

				if (!v4lcap.stop()) return;





				bt_start.set_label("start");
				cb_device.set_sensitive(true);
				cb_input.set_sensitive(true);
				cb_standard.set_sensitive(true);
				cb_frame_size.set_sensitive(true);
				cb_format_desc.set_sensitive(true);
				sp_width.set_sensitive(true);
				sp_height.set_sensitive(true);
				m_signal_start.emit(false);
			}

			return;

		}

		void V4LInterface::event_cb_device_changed() {

			if (v4lcap.is_opened()) {
				v4lcap.close();
			}

			Glib::ustring dev = cb_device.get_active_text();

			if (dev.size() < 1) return;

			if (v4lcap.open(dev.data())) {

				lb_device_name.set_text(dev.data());
				lb_device_card.set_text(v4lcap.get_device_card());
				lb_device_driver.set_text(v4lcap.get_device_driver());
				lb_device_bus.set_text(v4lcap.get_device_bus());

			} else {
				printf("Dureza: %s\n", dev.data());
			}

			__update_capabilities();

			// free memory =============================================================
			Gtk::Widget * page;

			page = notebook.get_nth_page(1);

			while (page) {
				notebook.remove_page(1);
				delete page;
				page = notebook.get_nth_page(1);
			}

			int size;
			__create_control_lists();

			size = ctrl_list_default.size();
			size = ctrl_list_user.size();
			size = ctrl_list_private.size();

			__make_control_table(ctrl_list_default, "Default");
			__make_control_table(ctrl_list_user, "User");
			__make_control_table(ctrl_list_private, "Private");

			__update_control_widgets(ctrl_list_default);
			__update_control_widgets(ctrl_list_user);
			__update_control_widgets(ctrl_list_private);

		}

		void V4LInterface::event_cb_input_changed() {

			if (cb_input.get_active_row_number() == -1) return;

			Gtk::TreeModel::iterator it = cb_input.get_active();

			Gtk::TreeModel::Row row = *it;
			struct v4l2_input input = row[model_input.m_col_data];

			v4lcap.set_input(input.index);

			__update_capabilities();

		}

		void V4LInterface::event_cb_standard_changed() {


			if (cb_standard.get_active_row_number() == -1) return;

			Gtk::TreeModel::iterator it = cb_standard.get_active();

			Gtk::TreeModel::Row row = *it;
			struct v4l2_standard standard = row[model_standard.m_col_data];

			v4lcap.set_standard(standard.id);

			__update_capabilities();

		}

		void V4LInterface::event_cb_format_desc_changed() {


			if (cb_format_desc.get_active_row_number() == -1) return;


			Gtk::TreeModel::iterator it = cb_format_desc.get_active();

			Gtk::TreeModel::Row row = *it;
			struct v4l2_fmtdesc fmtdesc = row[model_format_desc.m_col_data];

			v4lcap.set_pixel_format(fmtdesc.pixelformat);

			__update_capabilities();

		}

		void V4LInterface::event_cb_frame_size_changed() {


			if (cb_frame_size.get_active_row_number() == -1) return;


			Gtk::TreeModel::iterator it = cb_frame_size.get_active();

			Gtk::TreeModel::Row row = *it;
			struct v4l2_frmsizeenum frmsize = row[model_frame_size.m_col_data];

			v4lcap.set_frame_size(frmsize.discrete.width, frmsize.discrete.height);


			__update_capabilities();

		}


	}
}
